﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace Roton2.TextModeEmulation
{
    /// <summary>
    /// Renders characters for a CGA, EGA or VGA compatible text mode.
    /// </summary>
    public class TextRenderer : IRenderer
    {
        /// <summary>
        /// Raised when a character has been rendered and should be drawn onscreen.
        /// </summary>
        public event EventHandler<CharacterDrawnEventArgs> CharacterDrawn;

        int characterHeight;
        int characterWidth;
        CharacterGenerator generator;
        Int32[] palette;

        /// <summary>
        /// Create a screen renderer that uses a default font and palette.
        /// </summary>
        public TextRenderer()
        {
            byte[] font = Defaults.Font;
            this.characterHeight = font.Length / 256;
            this.characterWidth = 8;
            this.generator = new CharacterGenerator(Defaults.Font, characterWidth, characterHeight);
            this.palette = GetPalette(Defaults.Palette);
        }

        /// <summary>
        /// Create a text renderer with the given character dimensions, font and palette.
        /// </summary>
        public TextRenderer(int characterWidth, int characterHeight, byte[] font, Color[] palette)
        {
            this.characterHeight = characterHeight;
            this.characterWidth = characterWidth;
            this.generator = new CharacterGenerator(font, characterWidth, characterHeight);
            this.palette = GetPalette(palette);
        }

        /// <summary>
        /// Returns false. TextRenderer does not have a screen buffer to read from.
        /// </summary>
        virtual public bool CanRead
        {
            get { return false; }
        }

        /// <summary>
        /// Returns true. Characters can be written to this renderer.
        /// </summary>
        virtual public bool CanWrite
        {
            get { return true; }
        }

        /// <summary>
        /// Convert a list of colors into a list of raw palette values.
        /// </summary>
        Int32[] GetPalette(Color[] palette)
        {
            List<Int32> result = new List<int>();

            foreach (Color color in palette)
                result.Add(color.ToArgb());

            return result.ToArray();
        }

        /// <summary>
        /// Read a character at the specified location. This will always fail in TextRenderer.
        /// </summary>
        virtual public AnsiChar Read(int x, int y)
        {
            throw new InvalidOperationException();
        }

        /// <summary>
        /// Render a character at the specified location.
        /// </summary>
        virtual public void Write(int x, int y, AnsiChar ac)
        {
            Int32[] bits = generator.Generate(ac.Character, palette[ac.Color & 0x0F], palette[ac.Color >> 4]);
            PixelBuffer buffer = new PixelBuffer(characterWidth, characterHeight, bits);
            Point location = new Point(x * characterWidth, y * characterHeight);
            CharacterDrawn(this, new CharacterDrawnEventArgs(buffer.Bitmap, location));
        }
    }
}
